package com.zyb.ext.ioc;

import lombok.extern.slf4j.Slf4j;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.springframework.util.StringUtils;

import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * xml方式实现ioc的模拟
 */
@Slf4j
public class MyIocForXml {
    //储存单例对象，key：id
    private Map<String, Object> singletonMap = null;
    //储存多例Class，以便反射new新的对象，key：id
    private Map<String, Class<?>> classMap = null;
    //遍历bean下面的property节点值，key：id
    private Map<String, Element> propertyMap = null;
    //存储这个bean是单例还是多例对象，key：id
    private Map<String, String> beanScopeMap = null;

    public MyIocForXml(String xmlPath) throws ClassNotFoundException, DocumentException, InstantiationException, IllegalAccessException {
        singletonMap = new ConcurrentHashMap();
        classMap = new ConcurrentHashMap();
        propertyMap = new ConcurrentHashMap();
        beanScopeMap = new ConcurrentHashMap();
        //获取xml中所有的bean信息
        initIOC(xmlPath);

    }

    private void initIOC(String xmlPath) throws DocumentException, ClassNotFoundException, IllegalAccessException, InstantiationException {
        if (StringUtils.isEmpty(xmlPath)) {
            throw new RuntimeException("配置文件路径不得为空...");
        }
        //通过dom4j获取xml中的bean信息
        SAXReader saxReader = new SAXReader();
        InputStream resourceAsStream = getClass().getClassLoader().getResourceAsStream(xmlPath);
        Document read = saxReader.read(resourceAsStream);
        //获取xml的根节点
        Element rootElement = read.getRootElement();
        List<Element> elementList = rootElement.elements("bean");
        if (elementList == null || elementList.isEmpty()) {
            log.info("xml配置文件中没有定义任何bean节点...");
            return;
        }
        //遍历bean节点，获取相关的属性
        for (Element element : elementList) {
            String id = element.attributeValue("id");
            String className = element.attributeValue("class");
            if (StringUtils.isEmpty(id) || StringUtils.isEmpty(className)) {
                throw new RuntimeException("错误的定义bean信息，未指定id或class信息！");
            }
            Class<?> aClass = Class.forName(className);
            //获取定义的是多例还是单例，默认为单例
            String scope = element.attributeValue("scope");
            //如果scope为空，则认为是单例
            if (StringUtils.isEmpty(scope) || "singleton".equals(scope)) {
                //单例对象
                beanScopeMap.put(id, "singleton");
            } else if ("prototype".equals(scope)) {
                //多例对象
                beanScopeMap.put(id, scope);
            } else {
                throw new RuntimeException("scope 参数值非法，只能定义为singleton或prototype");
            }
            //储存对象的class值
            classMap.put(id, aClass);
            propertyMap.put(id, element);
        }
    }


    public Object getBean(String beanId) throws IllegalAccessException, InstantiationException, NoSuchFieldException {
        Object classObj = null;

        if (StringUtils.isEmpty(beanId)) {
            log.info("getBean 入参beanId不能为空！");
            return classObj;
        }
        Class<?> aClass = classMap.get(beanId);
        //单例对象
        String scope = beanScopeMap.get(beanId);
        if ("singleton".equals(scope)) {
            classObj = singletonMap.get(beanId);
            if (classObj == null) {
                classObj = aClass.newInstance();
                singletonMap.put(beanId, classObj);
            }
        }
        //多例对象
        if ("prototype".equals(scope)) {
            classObj = classMap.get(beanId).newInstance();
        }

        //获取属性值
        Element element = propertyMap.get(beanId);
        setPropertyList(classObj, aClass, element);
        return classObj;
    }

    //为对象注入属性
    private void setPropertyList(Object classObj, Class<?> aClass, Element element) throws NoSuchFieldException, IllegalAccessException {
        List<Element> propertyList = element.elements("property");
        if (propertyList != null && !propertyList.isEmpty()) {
            for (Element property : propertyList) {
                String name = property.attributeValue("name");
                String value = property.attributeValue("value");
                Field field = aClass.getDeclaredField(name);
                field.setAccessible(true);
                String fieldTypeName = field.getType().getName();
                if ("int".equals(fieldTypeName) || "java.lang.Integer".equals(fieldTypeName)) {
                    //转换为int类型并为该成员属性赋值
                    field.set(classObj, Integer.parseInt(value));
                }

                //判断该成员属性是否为String类型
                if ("java.lang.String".equals(fieldTypeName)) {
                    //为该成员属性赋值
                    field.set(classObj, value);
                }
            }
        }
    }

    //销毁方法
    public void destory() {
        singletonMap.clear();
        singletonMap = null;

        classMap.clear();
        classMap = null;

        propertyMap.clear();
        propertyMap = null;

        beanScopeMap.clear();
        beanScopeMap = null;
    }
}
